﻿using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Common
{
    /// <summary>
    /// 自动服务注入
    /// </summary>
    public static class InjectionExtension
    {
        /// <summary>
        /// 获取程序集版本
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        public static Version GetVersion(this System.Reflection.Assembly model)
        {
            var assemblyVersionAttributes = (System.Reflection.AssemblyVersionAttribute[])model.GetCustomAttributes(typeof(System.Reflection.AssemblyVersionAttribute), false);
            if (assemblyVersionAttributes != null && assemblyVersionAttributes.Length > 0)
            {
                var assemblyVersionAttribute = assemblyVersionAttributes[0];
                return Version.Parse(assemblyVersionAttribute.Version);
            }
            return Version.Parse("0");
        }

        /// <summary>
        /// 服务自动注入（依赖程序集）
        /// </summary>
        /// <param name="serviceCollection">需要自动注入服务的服务集合</param>
        /// <returns>自动注入服务后的服务集合</returns>
        public static IServiceCollection AutoInjection(this IServiceCollection serviceCollection)
        {
            var types = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes());
            Injection(serviceCollection, types);
            return serviceCollection;
        }

        /// <summary>
        /// 启用缓存，默认使用内存缓存，可选redis缓存
        /// </summary>
        /// <param name="serviceCollection"></param>
        /// <param name="cacheType"></param>
        /// <returns></returns>
        public static IServiceCollection UseCache(this IServiceCollection serviceCollection, CacheType cacheType = CacheType.MemoryCache)
        {
            if (cacheType == CacheType.MemoryCache)
            {
                serviceCollection.AddMemoryCache(); //内存缓存
            }
            else if (cacheType == CacheType.DistributedMemoryCache)
            {
                serviceCollection.AddDistributedMemoryCache(); //内存缓存（分布式）
            }
            else if (cacheType == CacheType.Other)
            {
                //其他缓存，如：redis
            }
            serviceCollection.AddSingleton(typeof(CacheType), cacheType);
            serviceCollection.AddSingleton(typeof(ICacheService), typeof(CacheService));
            return serviceCollection;
        }

        /// <summary>
        /// 启用Socket通信
        /// </summary>
        /// <param name="serviceCollection"></param>
        /// <param name="socketType"></param>
        /// <returns></returns>
        public static IServiceCollection UseSocket(this IServiceCollection serviceCollection, SocketType socketType = SocketType.All)
        {
            if (socketType == SocketType.Client)//客户端
            {
                serviceCollection.AddTransient(typeof(TcpClientWorker));
            }
            else if (socketType == SocketType.Server)//服务端
            {
                serviceCollection.AddTransient(typeof(TcpHostWorker));
                serviceCollection.AddTransient(typeof(TcpServerWorker));
            }
            else if (socketType == SocketType.All)//全部
            {
                serviceCollection.AddTransient(typeof(TcpClientWorker));
                serviceCollection.AddTransient(typeof(TcpHostWorker));
                serviceCollection.AddTransient(typeof(TcpServerWorker));
            }
            return serviceCollection;
        }

        /// <summary>
        /// 加载所有程序集
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        public static Assembly LoadFileAssembly(string file)
        {
            try
            {
                //将外部程序集载入到当前程序域中
                return AppDomain.CurrentDomain.Load(File.ReadAllBytes(file));
            }
            catch (Exception ex)
            {
                LogHelper.Error($"程序集{file}不可识别，{ex}");
            }
            return null;
        }

        /// <summary>
        /// 服务自动注入（插件程序集，不是程序依赖的程序集）
        /// </summary>
        /// <param name="serviceCollection">需要自动注入服务的服务集合</param>
        /// <param name="path">指定目录</param>
        /// <returns>自动注入服务后的服务集合</returns>
        public static IServiceCollection AutoInjection(this IServiceCollection serviceCollection, String path)
        {
            var types = Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories)
               .Select(LoadFileAssembly).Where(o => o != null)
               .SelectMany(a => a.GetTypes());
            Injection(serviceCollection, types);
            return serviceCollection;
        }

        /// <summary>
        ///  注入逻辑
        /// </summary>
        /// <param name="serviceCollection"></param>
        /// <param name="types"></param>
        private static void Injection(IServiceCollection serviceCollection, IEnumerable<Type> types)
        {
            foreach (var type in types)
            {
                if (serviceCollection.Count(o => o.ServiceType.FullName == type.FullName) > 0)
                {
                    continue;
                }

                var attribute = type.GetCustomAttribute<InjectionAttribute>();
                if (attribute == null)
                {
                    continue;
                }

                if (attribute.InterfaceType != null)//通过接口实现注入
                {
                    switch (attribute.InjectionType)
                    {
                        case InjectionType.Transient:
                            serviceCollection.AddTransient(attribute.InterfaceType, type);
                            break;
                        case InjectionType.Scoped:
                            serviceCollection.AddScoped(attribute.InterfaceType, type);
                            break;
                        case InjectionType.Singleton:
                            serviceCollection.AddSingleton(attribute.InterfaceType, type);
                            break;
                        default:
                            break;
                    }
                }
                else
                {
                    //通过类型实现注入
                    switch (attribute.InjectionType)
                    {
                        case InjectionType.Transient:
                            serviceCollection.AddTransient(type);
                            break;
                        case InjectionType.Scoped:
                            serviceCollection.AddScoped(type);
                            break;
                        case InjectionType.Singleton:
                            serviceCollection.AddSingleton(type);
                            break;
                        default:
                            break;
                    }
                }

            }
        }
    }
}
